package jwtsession

import (
	"context"
	"errors"
	"net/http"
	"time"

	"github.com/golang-jwt/jwt/v4"
)

type NotFoundError struct {
	label string
}

// Error implements the error interface.
func (e *NotFoundError) Error() string {
	return "session: " + e.label + " not found"
}

func IsNotFound(err error) bool {
	if err == nil {
		return false
	}
	var e *NotFoundError
	return errors.As(err, &e)
}

// 这里定义 sessionId 的读取和返回方式
type Inspector interface {
	Read(ctx context.Context, req *http.Request, name string) (sessionId string, err error)
	Write(ctx context.Context, name, sessionId string, options *Options, writter WritterFunc) error
}

type CookieInspector struct{}

func (inspector CookieInspector) Read(ctx context.Context, req *http.Request, name string) (sessionId string, err error) {
	// 从 cookie里读取
	cookie, err := req.Cookie(name)
	if err != nil || cookie.Value == "" {
		return "", &NotFoundError{"cookie中未读取到" + name}
	}
	return jwtParse(cookie.Value)
}

func (inspector CookieInspector) Write(ctx context.Context, name, sessionId string, options *Options, writter WritterFunc) error {
	//jwt signature
	jwtString, err := jwtSign(sessionId, options)
	if err != nil {
		return err
	}

	cookie := &http.Cookie{
		Name:     name,
		Value:    jwtString,
		Path:     options.Path,
		Domain:   options.Domain,
		MaxAge:   options.MaxAge,
		Secure:   options.Secure,
		HttpOnly: options.HttpOnly,
		SameSite: options.SameSite,
	}
	// 是否是 kratos
	return writter(ctx, "Set-Cookie", cookie.String())
}

// jwt
type HeaderInspector struct{}

var mySigningKey = []byte("growup666")

func (inspector HeaderInspector) Read(ctx context.Context, req *http.Request, name string) (sessionId string, err error) {
	// 从 cookie里读取
	jwtStr := req.Header.Get(name)
	if jwtStr != "" {
		//解析
		return jwtParse(jwtStr)
	}
	return "", &NotFoundError{"请求header中未读取到" + name}
}

func (inspector HeaderInspector) Write(ctx context.Context, name, sessionId string, options *Options, writter WritterFunc) error {
	//换成使用 jwt
	jwtString, err := jwtSign(sessionId, options)
	if err != nil {
		return err
	}
	return writter(ctx, name, jwtString)
}

func jwtSign(sessionId string, options *Options) (jwtString string, err error) {
	claims :=
		jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(options.MaxAge) * time.Second)),
			ID:        sessionId,
		}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	// fmt.Printf("write sessionId:%+v\n", sessionId)
	str, err := token.SignedString(mySigningKey)
	// fmt.Printf("write jwt:%+v\n", str)
	return str, err
}

func jwtParse(tokenString string) (string, error) {
	token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
		return mySigningKey, nil
	})

	if err != nil {
		return "", &NotFoundError{"session解析失败了:" + err.Error()}
	}

	if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid {
		return claims.ID, nil
	}
	return "", &NotFoundError{"session解析失败了:" + err.Error()}
}
